package utils;


import spring.exceptions.IllegalBeanException;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Objects;
import java.util.function.Function;

/**
 * 反射工具类
 *
 * @author richie696
 * @version 1.0
 * @since 2020/11/08
 */
public class ReflectUtils {

    private ReflectUtils() {
    }

    public static Object getParameterValue(Object obj, StringBuilder key)
            throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        if (obj == null || key.length() == 0) {
            return null;
        }
        if (key.charAt(0) == '.' || key.charAt(key.length() - 1) == '.') {
            LogUtils.error("参数[" + key + "]的key值有误，请检查参数是否合法。");
            return null;
        }
        String currentKey;
        if (key.indexOf(".") != -1) {
            currentKey = key.substring(0, key.indexOf("."));
            key.delete(0, currentKey.length() + 1);
        } else {
            currentKey = key.toString();
            key.delete(0, key.length());
        }
        Method getter = getMethod(obj, currentKey, MethodType.GETTER);
        getter.setAccessible(true);
        Object parameterObject = getter.invoke(obj);
        if (key.length() > 0) {
            return getParameterValue(parameterObject, key);
        }
        return parameterObject;
    }

    public static void setSimpleParameterValue(Object obj, String propertyName, Object value)
            throws Exception {
        Method setter = getMethod(obj, propertyName, MethodType.SETTER);
        setter.setAccessible(true);
        Class<?> parameterClass = ReflectUtils.getParameterClass(obj, propertyName);
        if (value.getClass() == String.class) {
            Object propertyValue = PrimitiveType.convertType(parameterClass, (String) value);
            setter.invoke(obj, propertyValue);
        } else {
            setter.invoke(obj, value);
        }
    }

    public static Class<?> getParameterClass(Object obj, String propertyName) throws NoSuchMethodException {
        Method setter = getMethod(obj, propertyName, MethodType.SETTER);
        setter.setAccessible(true);
        return setter.getParameterTypes()[0];
    }

    private static Method getMethod(Object obj, String propertyName, MethodType type) throws NoSuchMethodException {
        char[] chars = propertyName.toCharArray();
        chars[0] -= 32;
        String methodName;
        switch (type) {
            case GETTER:
                methodName = "get" + String.valueOf(chars);
                break;
            case SETTER:
                methodName = "set" + String.valueOf(chars);
                break;
            default:
                methodName = String.valueOf(chars);
        }
        Method[] methods = obj.getClass().getMethods();
        Method propertyMethod = null;
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                propertyMethod = method;
                break;
            }
        }
        if (Objects.isNull(propertyMethod)) {
            throw new NoSuchMethodException(String.format("在 %s 类当中未找到 %s 属性的 setter 方法",
                    obj.getClass().getName(), propertyName));
        }
        return propertyMethod;
    }

    public static boolean isImplementInterface(Class<?> cls) {
        boolean result;
        int count = cls.getInterfaces().length;
        if (count == 0) {
            Class<?> superClass = cls.getSuperclass();
            if (Objects.isNull(superClass) || superClass == Object.class) {
                return false;
            }
            result = isImplementInterface(superClass);
        } else {
            result = true;
        }
        return result;
    }

    public static boolean isImplementInterface(Class<?> cls, Class<?> interfaceCls) {
        boolean result = false;
        for (Class<?> anInterface : cls.getInterfaces()) {
            if (anInterface.equals(interfaceCls)) {
                result = true;
                break;
            }
            if (anInterface.getInterfaces().length > 0) {
                result = isImplementInterface(anInterface, interfaceCls);
                if (result) {
                    break;
                }
            }
        }
        if (!result) {
            Class<?> superClass = cls.getSuperclass();
            if (Objects.isNull(superClass) || superClass == Object.class) {
                return false;
            }
            result = isImplementInterface(superClass, interfaceCls);
        }
        return result;
    }

    public static boolean isPrimitive(Class<?> cls) {
        return (cls == String.class) || cls.isPrimitive();
    }

    public static Object createObject(String clsName) {
        Class<?> cls;
        Object object;
        try {
            cls = Class.forName(clsName);
            object = cls.getConstructor().newInstance();
        } catch (InstantiationException |
                IllegalAccessException |
                InvocationTargetException |
                NoSuchMethodException |
                ClassNotFoundException e) {
            throw new IllegalBeanException(e);
        }
        return object;
    }

    public static String convertName(String clsName) {
        if (clsName.contains(".")) {
            clsName = clsName.substring(clsName.lastIndexOf('.') + 1);
        }
        char[] chars = clsName.toCharArray();
        if (chars[0] >= 'a' && chars[0] <= 'z') {
            chars[0] -= 32;
        } else {
            chars[0] += 32;
        }
        return String.valueOf(chars);
    }

    private enum PrimitiveType {
        INT(int.class, Integer::parseInt),
        LONG(long.class, Long::parseLong),
        SHORT(short.class, Short::parseShort),
        BYTE(byte.class, Byte::parseByte),
        FLOAT(float.class, Float::parseFloat),
        DOUBLE(double.class, Double::parseDouble),
        BOOL(boolean.class, Boolean::parseBoolean),
        STRING(String.class, String::new),
        BOOLEAN(Boolean.class, Boolean::valueOf),
        OBJ_INTEGER(Integer.class, Integer::valueOf),
        OBJ_LONG(Integer.class, Long::valueOf),
        OBJ_SHORT(Short.class, Short::valueOf),
        BIG_INTEGER(BigInteger.class, value -> BigInteger.valueOf(Long.parseLong(value))),
        BIG_DECIMAL(BigDecimal.class, value -> BigDecimal.valueOf(Double.parseDouble(value)));

        private final Class<?> cls;
        private final Function<String, ?> function;

        PrimitiveType(Class<?> cls, Function<String, ?> function) {
            this.cls = cls;
            this.function = function;

        }

        public static Object convertType(Class<?> cls, String value) {
            PrimitiveType type = PrimitiveType.valueOf(cls);
            return type.function.apply(value);
        }

        public static PrimitiveType valueOf(Class<?> cls) {
            return Arrays.stream(values()).filter(o -> o.cls == cls).findFirst().orElseThrow(IllegalArgumentException::new);
        }
    }


    private enum MethodType {
        NONE, GETTER, SETTER
    }
}
